package com.zhentao.service.impl;

import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.http.Method;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * MinIO服务类
 */
@Service
public class MinioService {

    @Autowired
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;

    @Value("${minio.endpoint}")
    private String endpoint;

    /**
     * 上传视频到MinIO
     * @param file 视频文件
     * @param folder 文件夹路径（可选）
     * @return 视频访问URL
     */
    public String uploadVideo(MultipartFile file, String folder) throws Exception {
        return uploadFile(file, folder, "video");
    }

    /**
     * 上传图片到MinIO
     * @param file 图片文件
     * @param folder 文件夹路径（可选）
     * @return 图片访问URL
     */
    public String uploadImage(MultipartFile file, String folder) throws Exception {
        return uploadFile(file, folder, "image");
    }

    /**
     * 通用文件上传方法（支持自定义文件名）
     * @param file 文件
     * @param folder 文件夹路径（可选）
     * @param fileName 自定义文件名（可选）
     * @return 文件访问URL
     */
    public String uploadFileWithCustomName(MultipartFile file, String folder, String fileName) throws Exception {
        // 检查文件是否为空
        if (file.isEmpty()) {
            throw new RuntimeException("上传文件不能为空");
        }

        // 检查文件类型
        String contentType = file.getContentType();
        if (contentType == null) {
            throw new RuntimeException("无法识别文件类型");
        }

        // 确保视频文件的Content-Type正确
        if (contentType.startsWith("video/")) {
            contentType = getCorrectVideoContentType(file.getOriginalFilename(), contentType);
        }

        // 生成文件名
        if (fileName == null || fileName.isEmpty()) {
            String originalFilename = file.getOriginalFilename();
            String extension = "";
            if (originalFilename != null && originalFilename.contains(".")) {
                extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            }
            fileName = UUID.randomUUID().toString().replace("-", "") + extension;
        }

        // 构建对象名称
        String objectName = folder != null && !folder.isEmpty() ? folder + "/" + fileName : fileName;

        // 检查存储桶是否存在，不存在则创建
        ensureBucketExists();

        // 上传文件
        try (InputStream inputStream = file.getInputStream()) {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, file.getSize(), -1)
                            .contentType(contentType)
                            .build()
            );
        }

        // 返回文件访问URL
        return getFileUrl(objectName);
    }

    /**
     * 通用文件上传方法
     * @param file 文件
     * @param folder 文件夹路径（可选）
     * @param fileType 文件类型（image/video/any）
     * @return 文件访问URL
     */
    public String uploadFile(MultipartFile file, String folder, String fileType) throws Exception {
        // 检查文件是否为空
        if (file.isEmpty()) {
            throw new RuntimeException("上传文件不能为空");
        }

        // 检查文件类型
        String contentType = file.getContentType();
        if (contentType == null) {
            throw new RuntimeException("无法识别文件类型");
        }

        // 根据fileType参数进行类型验证
        if ("image".equals(fileType) && !contentType.startsWith("image/")) {
            throw new RuntimeException("只能上传图片文件");
        } else if ("video".equals(fileType) && !contentType.startsWith("video/")) {
            throw new RuntimeException("只能上传视频文件");
        } else if ("any".equals(fileType) && !contentType.startsWith("image/") && !contentType.startsWith("video/")) {
            throw new RuntimeException("只能上传图片或视频文件");
        }

        // 确保视频文件的Content-Type正确
        if (contentType.startsWith("video/")) {
            contentType = getCorrectVideoContentType(file.getOriginalFilename(), contentType);
        }

        // 生成唯一文件名
        String originalFilename = file.getOriginalFilename();
        String extension = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }
        String fileName = UUID.randomUUID().toString().replace("-", "") + extension;

        // 构建对象名称
        String objectName = folder != null && !folder.isEmpty() ? folder + "/" + fileName : fileName;

        // 检查存储桶是否存在，不存在则创建
        ensureBucketExists();

        // 上传文件
        try (InputStream inputStream = file.getInputStream()) {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, file.getSize(), -1)
                            .contentType(contentType)
                            .build()
            );
        }

        // 返回文件访问URL
        return getFileUrl(objectName);
    }

    /**
     * 获取正确的视频Content-Type
     */
    private String getCorrectVideoContentType(String filename, String originalContentType) {
        if (filename == null) {
            return originalContentType;
        }
        
        String extension = filename.toLowerCase();
        if (extension.endsWith(".mp4")) {
            return "video/mp4";
        } else if (extension.endsWith(".avi")) {
            return "video/x-msvideo";
        } else if (extension.endsWith(".mov")) {
            return "video/quicktime";
        } else if (extension.endsWith(".wmv")) {
            return "video/x-ms-wmv";
        } else if (extension.endsWith(".flv")) {
            return "video/x-flv";
        } else if (extension.endsWith(".webm")) {
            return "video/webm";
        } else if (extension.endsWith(".mkv")) {
            return "video/x-matroska";
        }
        
        return originalContentType;
    }

    /**
     * 获取文件访问URL
     * @param objectName 对象名称
     * @return 文件访问URL
     */
    private String getFileUrl(String objectName) {
        // 确保endpoint不以/结尾，objectName不以/开头
        String cleanEndpoint = endpoint.endsWith("/") ? endpoint.substring(0, endpoint.length() - 1) : endpoint;
        String cleanObjectName = objectName.startsWith("/") ? objectName.substring(1) : objectName;
        
        return cleanEndpoint + "/" + bucketName + "/" + cleanObjectName;
    }

    /**
     * 确保存储桶存在，不存在则创建
     */
    private void ensureBucketExists() throws Exception {
        boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!bucketExists) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            
            // 设置存储桶为公共读取策略
            setBucketPublicReadPolicy(bucketName);
        } else {
            // 即使存储桶存在，也确保策略设置正确
            try {
                setBucketPublicReadPolicy(bucketName);
            } catch (Exception e) {
                System.out.println("设置存储桶策略时出现警告: " + e.getMessage());
            }
        }
    }

    /**
     * 设置存储桶为公共读取策略
     * @param bucketName 存储桶名称
     */
    public void setBucketPublicReadPolicy(String bucketName) throws Exception {
        String policy = "{\n" +
                "  \"Version\": \"2012-10-17\",\n" +
                "  \"Statement\": [\n" +
                "    {\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Principal\": \"*\",\n" +
                "      \"Action\": \"s3:GetObject\",\n" +
                "      \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" +
                "    }\n" +
                "  ]\n" +
                "}";
        
        minioClient.setBucketPolicy(
            SetBucketPolicyArgs.builder()
                .bucket(bucketName)
                .config(policy)
                .build()
        );
        
        System.out.println("存储桶 " + bucketName + " 已设置为公共读取策略");
    }

    /**
     * 获取文件的预签名URL（用于临时访问）
     * @param objectName 对象名称
     * @param expiry 过期时间（秒）
     * @return 预签名URL
     */
    public String getPresignedUrl(String objectName, int expiry) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expiry)
                        .build()
        );
    }

    /**
     * 获取文件信息
     * @param objectName 对象名称
     * @return 文件信息
     */
    public Map<String, Object> getFileInfo(String objectName) throws Exception {
        try {
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
            
            Map<String, Object> info = new HashMap<>();
            info.put("exists", true);
            info.put("size", stat.size());
            info.put("contentType", stat.contentType());
            info.put("lastModified", stat.lastModified());
            info.put("url", getFileUrl(objectName));
            info.put("presignedUrl", getPresignedUrl(objectName, 3600));
            
            return info;
        } catch (ErrorResponseException e) {
            if (e.errorResponse().code().equals("NoSuchKey")) {
                Map<String, Object> info = new HashMap<>();
                info.put("exists", false);
                return info;
            }
            throw e;
        }
    }

    /**
     * 删除MinIO中的文件
     * @param objectName 对象名称
     */
    public void deleteFile(String objectName) throws Exception {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()
        );
    }

    /**
     * 检查文件是否存在
     * @param objectName 对象名称
     * @return 是否存在
     */
    public boolean fileExists(String objectName) throws Exception {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
            return true;
        } catch (ErrorResponseException e) {
            if (e.errorResponse().code().equals("NoSuchKey")) {
                return false;
            }
            throw e;
        }
    }

    /**
     * 列出所有存储桶（用于测试连接）
     */
    public void listBuckets() throws Exception {
        minioClient.listBuckets();
    }

    /**
     * 测试MinIO连接
     */
    public void testConnection() throws Exception {
        try {
            listBuckets();
            System.out.println("✓ MinIO连接测试成功");
        } catch (Exception e) {
            System.err.println("✗ MinIO连接测试失败: " + e.getMessage());
            throw e;
        }
    }
} 